<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>JVM | 烟云流</title>
    <meta name="generator" content="VuePress 1.5.2">
    <link rel="icon" href="/img/favicon.ico">
    <meta name="description" content="web后端技术博客,简洁至上,专注web后端学习与总结。JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github等技术文章。">
    <meta name="keywords" content="后端博客,个人技术博客,后端,后端开发,后端框架,web后端,后端面试题,技术文档,学习,面试,JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github,markdown">
    <meta name="baidu-site-verification" content="7F55weZDDc">
    <meta name="theme-color" content="#11a8cd">
    <link rel="preload" href="/assets/css/0.styles.5785bc9d.css" as="style"><link rel="preload" href="/assets/js/app.c45b0dab.js" as="script"><link rel="preload" href="/assets/js/2.921829d9.js" as="script"><link rel="preload" href="/assets/js/92.812c6c6e.js" as="script"><link rel="prefetch" href="/assets/js/10.75adca2d.js"><link rel="prefetch" href="/assets/js/100.808a2549.js"><link rel="prefetch" href="/assets/js/101.d7b51e05.js"><link rel="prefetch" href="/assets/js/102.bc7be85c.js"><link rel="prefetch" href="/assets/js/103.0912e59c.js"><link rel="prefetch" href="/assets/js/104.b4192a5e.js"><link rel="prefetch" href="/assets/js/105.7000e0bf.js"><link rel="prefetch" href="/assets/js/106.19bdf65d.js"><link rel="prefetch" href="/assets/js/107.c4aca87f.js"><link rel="prefetch" href="/assets/js/108.a415f4be.js"><link rel="prefetch" href="/assets/js/109.d8059458.js"><link rel="prefetch" href="/assets/js/11.074a2ca9.js"><link rel="prefetch" href="/assets/js/110.6ebc4f0d.js"><link rel="prefetch" href="/assets/js/111.f804021a.js"><link rel="prefetch" href="/assets/js/112.63f037f8.js"><link rel="prefetch" href="/assets/js/113.c96b3989.js"><link rel="prefetch" href="/assets/js/114.73e27e61.js"><link rel="prefetch" href="/assets/js/115.07767103.js"><link rel="prefetch" href="/assets/js/116.eb9b5b63.js"><link rel="prefetch" href="/assets/js/117.c70d0968.js"><link rel="prefetch" href="/assets/js/118.216818de.js"><link rel="prefetch" href="/assets/js/119.35e4cd6c.js"><link rel="prefetch" href="/assets/js/12.44baff08.js"><link rel="prefetch" href="/assets/js/120.e1da9150.js"><link rel="prefetch" href="/assets/js/121.06733a77.js"><link rel="prefetch" href="/assets/js/122.7cfbb6d4.js"><link rel="prefetch" href="/assets/js/123.0b7f48c6.js"><link rel="prefetch" href="/assets/js/124.02150a6e.js"><link rel="prefetch" href="/assets/js/125.b0deb813.js"><link rel="prefetch" href="/assets/js/126.bcff13d3.js"><link rel="prefetch" href="/assets/js/127.3c6291ff.js"><link rel="prefetch" href="/assets/js/128.697cf3a9.js"><link rel="prefetch" href="/assets/js/129.5fbd02d8.js"><link rel="prefetch" href="/assets/js/13.e4a3babe.js"><link rel="prefetch" href="/assets/js/130.c99e0ddf.js"><link rel="prefetch" href="/assets/js/131.0bb4f4ae.js"><link rel="prefetch" href="/assets/js/132.5ad93ff6.js"><link rel="prefetch" href="/assets/js/133.b7cdd049.js"><link rel="prefetch" href="/assets/js/134.cb4ff055.js"><link rel="prefetch" href="/assets/js/135.962cb545.js"><link rel="prefetch" href="/assets/js/136.3367dc6b.js"><link rel="prefetch" href="/assets/js/137.49b339b8.js"><link rel="prefetch" href="/assets/js/138.1892f708.js"><link rel="prefetch" href="/assets/js/139.38e8f89f.js"><link rel="prefetch" href="/assets/js/14.ead319e8.js"><link rel="prefetch" href="/assets/js/140.85fa474f.js"><link rel="prefetch" href="/assets/js/141.f5f6f9ae.js"><link rel="prefetch" href="/assets/js/142.49d1859a.js"><link rel="prefetch" href="/assets/js/143.771d99b7.js"><link rel="prefetch" href="/assets/js/144.9b1ba0d1.js"><link rel="prefetch" href="/assets/js/145.509dbf1b.js"><link rel="prefetch" href="/assets/js/146.8c583a4d.js"><link rel="prefetch" href="/assets/js/147.1e913648.js"><link rel="prefetch" href="/assets/js/148.113f1570.js"><link rel="prefetch" href="/assets/js/149.67056dad.js"><link rel="prefetch" href="/assets/js/15.b417e1d1.js"><link rel="prefetch" href="/assets/js/150.dae42cd2.js"><link rel="prefetch" href="/assets/js/151.eb59c9bb.js"><link rel="prefetch" href="/assets/js/152.5a41e373.js"><link rel="prefetch" href="/assets/js/153.9803a1c4.js"><link rel="prefetch" href="/assets/js/154.a61ba24e.js"><link rel="prefetch" href="/assets/js/155.6813f7ce.js"><link rel="prefetch" href="/assets/js/156.eaf956bd.js"><link rel="prefetch" href="/assets/js/157.451ba9c0.js"><link rel="prefetch" href="/assets/js/158.801b95bf.js"><link rel="prefetch" href="/assets/js/159.c6f24108.js"><link rel="prefetch" href="/assets/js/16.73d87194.js"><link rel="prefetch" href="/assets/js/160.46f72e56.js"><link rel="prefetch" href="/assets/js/161.48ce51ff.js"><link rel="prefetch" href="/assets/js/162.f9dfc838.js"><link rel="prefetch" href="/assets/js/163.f5333fd8.js"><link rel="prefetch" href="/assets/js/164.27591ef4.js"><link rel="prefetch" href="/assets/js/165.334214bb.js"><link rel="prefetch" href="/assets/js/166.ab89c52b.js"><link rel="prefetch" href="/assets/js/167.59c09561.js"><link rel="prefetch" href="/assets/js/168.c84053c3.js"><link rel="prefetch" href="/assets/js/169.4853ed63.js"><link rel="prefetch" href="/assets/js/17.32247c99.js"><link rel="prefetch" href="/assets/js/170.323d3037.js"><link rel="prefetch" href="/assets/js/171.fda43bb4.js"><link rel="prefetch" href="/assets/js/172.383dcb0a.js"><link rel="prefetch" href="/assets/js/173.d384fcd1.js"><link rel="prefetch" href="/assets/js/174.7b50cf40.js"><link rel="prefetch" href="/assets/js/175.afedd2db.js"><link rel="prefetch" href="/assets/js/176.a6b42645.js"><link rel="prefetch" href="/assets/js/177.bf665de9.js"><link rel="prefetch" href="/assets/js/178.236b8d2c.js"><link rel="prefetch" href="/assets/js/179.acbcf086.js"><link rel="prefetch" href="/assets/js/18.ede54d6a.js"><link rel="prefetch" href="/assets/js/180.8d08cff1.js"><link rel="prefetch" href="/assets/js/181.2159173e.js"><link rel="prefetch" href="/assets/js/182.0a8b2d61.js"><link rel="prefetch" href="/assets/js/183.e52d2665.js"><link rel="prefetch" href="/assets/js/184.b286f0ff.js"><link rel="prefetch" href="/assets/js/185.b2661b00.js"><link rel="prefetch" href="/assets/js/186.5cd5b396.js"><link rel="prefetch" href="/assets/js/187.1f964e51.js"><link rel="prefetch" href="/assets/js/188.99984140.js"><link rel="prefetch" href="/assets/js/189.dea2d86e.js"><link rel="prefetch" href="/assets/js/19.69462d4a.js"><link rel="prefetch" href="/assets/js/190.1fca141b.js"><link rel="prefetch" href="/assets/js/191.5a9f670f.js"><link rel="prefetch" href="/assets/js/192.9792a163.js"><link rel="prefetch" href="/assets/js/193.674d8d39.js"><link rel="prefetch" href="/assets/js/194.a3e37861.js"><link rel="prefetch" href="/assets/js/195.05207707.js"><link rel="prefetch" href="/assets/js/196.771dbe06.js"><link rel="prefetch" href="/assets/js/197.f22e148f.js"><link rel="prefetch" href="/assets/js/198.5aedd442.js"><link rel="prefetch" href="/assets/js/199.d1ad2e96.js"><link rel="prefetch" href="/assets/js/20.703f10f6.js"><link rel="prefetch" href="/assets/js/200.c7f6cdfc.js"><link rel="prefetch" href="/assets/js/201.08b3f193.js"><link rel="prefetch" href="/assets/js/202.faf6142e.js"><link rel="prefetch" href="/assets/js/203.8fa9a312.js"><link rel="prefetch" href="/assets/js/204.5857c0b0.js"><link rel="prefetch" href="/assets/js/205.7aa04c59.js"><link rel="prefetch" href="/assets/js/206.374b3e2d.js"><link rel="prefetch" href="/assets/js/207.2edb32c4.js"><link rel="prefetch" href="/assets/js/208.044ff9ab.js"><link rel="prefetch" href="/assets/js/209.a515fb0d.js"><link rel="prefetch" href="/assets/js/21.084411c0.js"><link rel="prefetch" href="/assets/js/210.08a5d88f.js"><link rel="prefetch" href="/assets/js/211.a2c6e887.js"><link rel="prefetch" href="/assets/js/212.ed9d835f.js"><link rel="prefetch" href="/assets/js/213.4b8b7ca1.js"><link rel="prefetch" href="/assets/js/214.db2faa11.js"><link rel="prefetch" href="/assets/js/215.b6608571.js"><link rel="prefetch" href="/assets/js/216.16980de5.js"><link rel="prefetch" href="/assets/js/217.b315df17.js"><link rel="prefetch" href="/assets/js/218.d5b94402.js"><link rel="prefetch" href="/assets/js/219.61b7375a.js"><link rel="prefetch" href="/assets/js/22.6b0bfaf6.js"><link rel="prefetch" href="/assets/js/220.0787caa2.js"><link rel="prefetch" href="/assets/js/221.dc2bb77d.js"><link rel="prefetch" href="/assets/js/222.c9bcd74b.js"><link rel="prefetch" href="/assets/js/223.5dae4e0d.js"><link rel="prefetch" href="/assets/js/224.7eddef6b.js"><link rel="prefetch" href="/assets/js/225.0c7c2d04.js"><link rel="prefetch" href="/assets/js/226.a480a214.js"><link rel="prefetch" href="/assets/js/23.0a5223a2.js"><link rel="prefetch" href="/assets/js/24.bddf0486.js"><link rel="prefetch" href="/assets/js/25.f4fe6de3.js"><link rel="prefetch" href="/assets/js/26.0984044b.js"><link rel="prefetch" href="/assets/js/27.d7686be0.js"><link rel="prefetch" href="/assets/js/28.a4110e15.js"><link rel="prefetch" href="/assets/js/29.a0454455.js"><link rel="prefetch" href="/assets/js/3.2ab7db4b.js"><link rel="prefetch" href="/assets/js/30.58bdf2b1.js"><link rel="prefetch" href="/assets/js/31.df31f9d4.js"><link rel="prefetch" href="/assets/js/32.70b86e96.js"><link rel="prefetch" href="/assets/js/33.1a62eb3d.js"><link rel="prefetch" href="/assets/js/34.90ae5557.js"><link rel="prefetch" href="/assets/js/35.04a13a69.js"><link rel="prefetch" href="/assets/js/36.5acb65b5.js"><link rel="prefetch" href="/assets/js/37.4ceaa53a.js"><link rel="prefetch" href="/assets/js/38.b53acc97.js"><link rel="prefetch" href="/assets/js/39.8a99d262.js"><link rel="prefetch" href="/assets/js/4.5f72d253.js"><link rel="prefetch" href="/assets/js/40.3505a22f.js"><link rel="prefetch" href="/assets/js/41.8260a3b9.js"><link rel="prefetch" href="/assets/js/42.23e8007d.js"><link rel="prefetch" href="/assets/js/43.cf92d177.js"><link rel="prefetch" href="/assets/js/44.07f0526e.js"><link rel="prefetch" href="/assets/js/45.d794ab0c.js"><link rel="prefetch" href="/assets/js/46.bb5441ab.js"><link rel="prefetch" href="/assets/js/47.b92acc49.js"><link rel="prefetch" href="/assets/js/48.742c442e.js"><link rel="prefetch" href="/assets/js/49.9ad9211e.js"><link rel="prefetch" href="/assets/js/5.0af1442c.js"><link rel="prefetch" href="/assets/js/50.a19abf33.js"><link rel="prefetch" href="/assets/js/51.17b9c85e.js"><link rel="prefetch" href="/assets/js/52.d42f0a21.js"><link rel="prefetch" href="/assets/js/53.a65f1ca3.js"><link rel="prefetch" href="/assets/js/54.c00a1671.js"><link rel="prefetch" href="/assets/js/55.a26c6c65.js"><link rel="prefetch" href="/assets/js/56.17dfab95.js"><link rel="prefetch" href="/assets/js/57.942366b8.js"><link rel="prefetch" href="/assets/js/58.87b4f991.js"><link rel="prefetch" href="/assets/js/59.35df3cd9.js"><link rel="prefetch" href="/assets/js/6.8921b3bb.js"><link rel="prefetch" href="/assets/js/60.329ba08b.js"><link rel="prefetch" href="/assets/js/61.b7ca60b1.js"><link rel="prefetch" href="/assets/js/62.aaecbd6c.js"><link rel="prefetch" href="/assets/js/63.596c1ec0.js"><link rel="prefetch" href="/assets/js/64.b176d7da.js"><link rel="prefetch" href="/assets/js/65.72974db7.js"><link rel="prefetch" href="/assets/js/66.3c775d28.js"><link rel="prefetch" href="/assets/js/67.393574c3.js"><link rel="prefetch" href="/assets/js/68.96edf798.js"><link rel="prefetch" href="/assets/js/69.d75ef477.js"><link rel="prefetch" href="/assets/js/7.aacc1c8b.js"><link rel="prefetch" href="/assets/js/70.a96d7486.js"><link rel="prefetch" href="/assets/js/71.dfce89d7.js"><link rel="prefetch" href="/assets/js/72.bbf04c65.js"><link rel="prefetch" href="/assets/js/73.9a217904.js"><link rel="prefetch" href="/assets/js/74.d86f0d4d.js"><link rel="prefetch" href="/assets/js/75.e0282382.js"><link rel="prefetch" href="/assets/js/76.f7c47533.js"><link rel="prefetch" href="/assets/js/77.660b26cb.js"><link rel="prefetch" href="/assets/js/78.65ec3c3d.js"><link rel="prefetch" href="/assets/js/79.65a1f3e3.js"><link rel="prefetch" href="/assets/js/8.511a5829.js"><link rel="prefetch" href="/assets/js/80.e1b3a7fc.js"><link rel="prefetch" href="/assets/js/81.1b2da77f.js"><link rel="prefetch" href="/assets/js/82.aaffb035.js"><link rel="prefetch" href="/assets/js/83.98a301cb.js"><link rel="prefetch" href="/assets/js/84.5e9406cd.js"><link rel="prefetch" href="/assets/js/85.967bcbfd.js"><link rel="prefetch" href="/assets/js/86.15d0038e.js"><link rel="prefetch" href="/assets/js/87.b62e00f6.js"><link rel="prefetch" href="/assets/js/88.c3cbf00c.js"><link rel="prefetch" href="/assets/js/89.e0d29f00.js"><link rel="prefetch" href="/assets/js/9.218d929a.js"><link rel="prefetch" href="/assets/js/90.2e28d1a7.js"><link rel="prefetch" href="/assets/js/91.4edd649e.js"><link rel="prefetch" href="/assets/js/93.15cb00ca.js"><link rel="prefetch" href="/assets/js/94.e00eeb74.js"><link rel="prefetch" href="/assets/js/95.2e2594ac.js"><link rel="prefetch" href="/assets/js/96.5bb3330f.js"><link rel="prefetch" href="/assets/js/97.9802378e.js"><link rel="prefetch" href="/assets/js/98.0cf2f146.js"><link rel="prefetch" href="/assets/js/99.85ec2257.js">
    <link rel="stylesheet" href="/assets/css/0.styles.5785bc9d.css">
  </head>
  <body class="theme-mode-light">
    <div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu have-body-img"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="/img/EB-logo.png" alt="烟云流" class="logo"> <span class="site-name can-hide">烟云流</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link">首页</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="java" class="dropdown-title"><a href="/java/" class="link-title">java</a> <span class="title" style="display:none;">java</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/jvm/" aria-current="page" class="nav-link router-link-exact-active router-link-active">JVM</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端" class="dropdown-title"><a href="/web/" class="link-title">前端</a> <span class="title" style="display:none;">前端</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>前端文章</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8143cc480faf9a11/" class="nav-link">JavaScript</a></li><li class="dropdown-subitem"><a href="/pages/802a1ca6f7b71c59/" class="nav-link">Vue</a></li></ul></li><li class="dropdown-item"><h4>学习笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/note/javascript/" class="nav-link">《JavaScript教程》笔记</a></li><li class="dropdown-subitem"><a href="/note/js/" class="nav-link">《JavaScript高级程序设计》笔记</a></li><li class="dropdown-subitem"><a href="/note/es6/" class="nav-link">《ES6 教程》笔记</a></li><li class="dropdown-subitem"><a href="/note/vue/" class="nav-link">《Vue》笔记</a></li><li class="dropdown-subitem"><a href="/note/typescript-axios/" class="nav-link">《TypeScript 从零实现 axios》</a></li><li class="dropdown-subitem"><a href="/note/git/" class="nav-link">《Git》学习笔记</a></li><li class="dropdown-subitem"><a href="/pages/51afd6/" class="nav-link">《TypeScript》笔记</a></li></ul></li><li class="dropdown-item"><h4>页面</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8309a5b876fc95e3/" class="nav-link">HTML</a></li><li class="dropdown-subitem"><a href="/pages/0a83b083bdf257cb/" class="nav-link">CSS</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技术" class="dropdown-title"><a href="/technology/" class="link-title">技术</a> <span class="title" style="display:none;">技术</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/9a7ee40fc232253e/" class="nav-link">技术文档</a></li><li class="dropdown-item"><!----> <a href="/pages/4c778760be26d8b3/" class="nav-link">GitHub技巧</a></li><li class="dropdown-item"><!----> <a href="/pages/117708e0af7f0bd9/" class="nav-link">Nodejs</a></li><li class="dropdown-item"><!----> <a href="/pages/41f87d890d0a02af/" class="nav-link">博客搭建</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="更多" class="dropdown-title"><a href="/more/" class="link-title">更多</a> <span class="title" style="display:none;">更多</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/f2a556/" class="nav-link">学习</a></li><li class="dropdown-item"><!----> <a href="/pages/aea6571b7a8bae86/" class="nav-link">面试</a></li><li class="dropdown-item"><!----> <a href="/pages/2d615df9a36a98ed/" class="nav-link">心情杂货</a></li><li class="dropdown-item"><!----> <a href="/pages/baaa02/" class="nav-link">实用技巧</a></li><li class="dropdown-item"><!----> <a href="/friends/" class="nav-link">友情链接</a></li></ul></div></div><div class="nav-item"><a href="/about/" class="nav-link">关于</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="收藏" class="dropdown-title"><a href="/pages/beb6c0bd8a66cea6/" class="link-title">收藏</a> <span class="title" style="display:none;">收藏</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/beb6c0bd8a66cea6/" class="nav-link">网站</a></li><li class="dropdown-item"><!----> <a href="/pages/eee83a9211a70f9d/" class="nav-link">资源</a></li><li class="dropdown-item"><!----> <a href="/pages/12df8ace52d493f6/" class="nav-link">Vue资源</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="索引" class="dropdown-title"><a href="/archives/" class="link-title">索引</a> <span class="title" style="display:none;">索引</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/categories/" class="nav-link">分类</a></li><li class="dropdown-item"><!----> <a href="/tags/" class="nav-link">标签</a></li><li class="dropdown-item"><!----> <a href="/archives/" class="nav-link">归档</a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar" style="display:none;"><div class="blogger"><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/IMG_20180415_162128.jpg"> <div class="blogger-info"><h3>YanYunLiu</h3> <span>让熟知嵌入陌生，长此以往也</span></div></div> <nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link">首页</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="java" class="dropdown-title"><a href="/java/" class="link-title">java</a> <span class="title" style="display:none;">java</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/jvm/" aria-current="page" class="nav-link router-link-exact-active router-link-active">JVM</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端" class="dropdown-title"><a href="/web/" class="link-title">前端</a> <span class="title" style="display:none;">前端</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>前端文章</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8143cc480faf9a11/" class="nav-link">JavaScript</a></li><li class="dropdown-subitem"><a href="/pages/802a1ca6f7b71c59/" class="nav-link">Vue</a></li></ul></li><li class="dropdown-item"><h4>学习笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/note/javascript/" class="nav-link">《JavaScript教程》笔记</a></li><li class="dropdown-subitem"><a href="/note/js/" class="nav-link">《JavaScript高级程序设计》笔记</a></li><li class="dropdown-subitem"><a href="/note/es6/" class="nav-link">《ES6 教程》笔记</a></li><li class="dropdown-subitem"><a href="/note/vue/" class="nav-link">《Vue》笔记</a></li><li class="dropdown-subitem"><a href="/note/typescript-axios/" class="nav-link">《TypeScript 从零实现 axios》</a></li><li class="dropdown-subitem"><a href="/note/git/" class="nav-link">《Git》学习笔记</a></li><li class="dropdown-subitem"><a href="/pages/51afd6/" class="nav-link">《TypeScript》笔记</a></li></ul></li><li class="dropdown-item"><h4>页面</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8309a5b876fc95e3/" class="nav-link">HTML</a></li><li class="dropdown-subitem"><a href="/pages/0a83b083bdf257cb/" class="nav-link">CSS</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技术" class="dropdown-title"><a href="/technology/" class="link-title">技术</a> <span class="title" style="display:none;">技术</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/9a7ee40fc232253e/" class="nav-link">技术文档</a></li><li class="dropdown-item"><!----> <a href="/pages/4c778760be26d8b3/" class="nav-link">GitHub技巧</a></li><li class="dropdown-item"><!----> <a href="/pages/117708e0af7f0bd9/" class="nav-link">Nodejs</a></li><li class="dropdown-item"><!----> <a href="/pages/41f87d890d0a02af/" class="nav-link">博客搭建</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="更多" class="dropdown-title"><a href="/more/" class="link-title">更多</a> <span class="title" style="display:none;">更多</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/f2a556/" class="nav-link">学习</a></li><li class="dropdown-item"><!----> <a href="/pages/aea6571b7a8bae86/" class="nav-link">面试</a></li><li class="dropdown-item"><!----> <a href="/pages/2d615df9a36a98ed/" class="nav-link">心情杂货</a></li><li class="dropdown-item"><!----> <a href="/pages/baaa02/" class="nav-link">实用技巧</a></li><li class="dropdown-item"><!----> <a href="/friends/" class="nav-link">友情链接</a></li></ul></div></div><div class="nav-item"><a href="/about/" class="nav-link">关于</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="收藏" class="dropdown-title"><a href="/pages/beb6c0bd8a66cea6/" class="link-title">收藏</a> <span class="title" style="display:none;">收藏</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/beb6c0bd8a66cea6/" class="nav-link">网站</a></li><li class="dropdown-item"><!----> <a href="/pages/eee83a9211a70f9d/" class="nav-link">资源</a></li><li class="dropdown-item"><!----> <a href="/pages/12df8ace52d493f6/" class="nav-link">Vue资源</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="索引" class="dropdown-title"><a href="/archives/" class="link-title">索引</a> <span class="title" style="display:none;">索引</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/categories/" class="nav-link">分类</a></li><li class="dropdown-item"><!----> <a href="/tags/" class="nav-link">标签</a></li><li class="dropdown-item"><!----> <a href="/archives/" class="nav-link">归档</a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/pages/jvm/" aria-current="page" class="active sidebar-link">JVM</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/jvm/#the-relation-of-jdk-jre-jvm" class="sidebar-link">The relation of JDK/JRE/JVM</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#子系统" class="sidebar-link">子系统</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/jvm/#类加载器子系统（class-loader-subsystem）：" class="sidebar-link">类加载器子系统（class loader subsystem）：</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#执行引擎（executionengine子系统）：" class="sidebar-link">执行引擎（Executionengine子系统）：</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/jvm/#运行时数据区域-runtimedataarea-组件" class="sidebar-link">运行时数据区域(Runtimedataarea)组件</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/jvm/#程序计数器（program-counter-register）" class="sidebar-link">程序计数器（Program Counter Register）:</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#java虚拟机栈（java-virtual-machine-stack）：" class="sidebar-link">Java虚拟机栈（Java Virtual Machine Stack）：</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#本地方法栈（native-method-stack）：" class="sidebar-link">本地方法栈（Native Method Stack）：</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#java-堆（java-heap）：" class="sidebar-link">Java 堆（Java Heap）：</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#方法区（method-area）：" class="sidebar-link">方法区（Method Area）：</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/jvm/#oom（内存溢出）" class="sidebar-link">OOM（内存溢出）</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/jvm/#outofmemoryerror：-permgen-space" class="sidebar-link">OutOfMemoryError： PermGen space</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#outofmemoryerror：-java-heap-space" class="sidebar-link">OutOfMemoryError：  Java heap space</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#outofmemoryerror：unable-to-create-new-native-thread" class="sidebar-link">OutOfMemoryError：unable to create new native thread</a></li><li class="sidebar-sub-header"><a href="/pages/jvm/#内存泄露" class="sidebar-link">内存泄露</a></li></ul></li></ul></li></ul> </aside> <div><main class="page"> <div class="theme-vdoing-wrapper "><div class="articleInfo-wrap" data-v-1b7c64bb><div class="articleInfo" data-v-1b7c64bb><ul class="breadcrumbs" data-v-1b7c64bb><li data-v-1b7c64bb><a href="/" title="首页" class="iconfont icon-home router-link-active" data-v-1b7c64bb></a></li> <li data-v-1b7c64bb><a href="/categories/?category=java" title="分类" data-v-1b7c64bb>java</a></li> <!----></ul> <div class="info" data-v-1b7c64bb><div title="作者" class="author iconfont icon-touxiang" data-v-1b7c64bb><a href="https://gitee.com/yanyunliu" target="_blank" title="作者" class="beLink" data-v-1b7c64bb>YanYunLiu</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-1b7c64bb><a href="javascript:;" data-v-1b7c64bb>2021-02-10</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-content"></div></div></div> <h1><img src="">
          JVM
        </h1> <div class="theme-vdoing-content content__default"><h2 id="the-relation-of-jdk-jre-jvm"><a href="#the-relation-of-jdk-jre-jvm" class="header-anchor">#</a> The relation of JDK/JRE/JVM</h2> <p>https://docs.oracle.com/javase/8/docs/index.html</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/image-20210210092420189.png" alt="image-20210210092420189"></p> <h2 id="子系统"><a href="#子系统" class="header-anchor">#</a> 子系统</h2> <h3 id="类加载器子系统（class-loader-subsystem）："><a href="#类加载器子系统（class-loader-subsystem）：" class="header-anchor">#</a> 类加载器子系统（class loader subsystem）：</h3> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/image-20210210100833136.png" alt="image-20210210100833136"></p> <h4 id="加载流程"><a href="#加载流程" class="header-anchor">#</a> 加载流程</h4> <h5 id="装载-load"><a href="#装载-load" class="header-anchor">#</a> 装载(Load)</h5> <p>查找和导入class文件</p> <p>​	(1)根据给定的全限定名类名(如java.lang.Object)来装载class文件的内容到Runtimedataarea中的methodarea(方法区域)。Java程序员可以extends java.lang.ClassLoader类来写自己的Classloader。</p> <blockquote><p>(1)通过一个类的全限定名获取定义此类的二进制字节流</p> <p>(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构</p> <p>(3)在Java堆中生成一个代表这个类的java.lang.Class对象，作为对方法区中这些数据的访问入口</p></blockquote> <p>(2) 对（1）中的加载过程是：当一个classloader启动时，classloader的生存地点在jvm中的堆，然后它去主机硬盘上去装载A.class到jvm的methodarea(方法区),方法区中的这个字节文件会被虚拟机拿来new A字节码，然后在堆内存生成了一个A字节码的对象，然后A字节码这个内存文件有两个引用，一个指向A的class对象，一个指向加载自己的classloader。见下图：</p> <p><img src="https://cdn.nlark.com/yuque/0/2020/jpeg/283692/1584675758218-7a5176c0-fe60-494e-90d4-19b1684f85a9.jpeg" alt="img"></p> <h5 id="链接-link"><a href="#链接-link" class="header-anchor">#</a> 链接(Link)</h5> <h6 id="（1）验证-verify"><a href="#（1）验证-verify" class="header-anchor">#</a> （1）验证(Verify)</h6> <p>保证被加载类的正确性</p> <p>文件格式验证、元数据验证、字节码验证、符号引用验证</p> <h6 id="（2）准备-prepare"><a href="#（2）准备-prepare" class="header-anchor">#</a> （2）准备(Prepare)</h6> <p>为类的静态变量分配内存，并将其初始化为默认值</p> <h6 id="（3）解析-resolve"><a href="#（3）解析-resolve" class="header-anchor">#</a> （3）解析(Resolve)</h6> <p>把类中的符号引用转换为直接引用</p> <h5 id="初始化-initialize"><a href="#初始化-initialize" class="header-anchor">#</a> 初始化(Initialize)</h5> <p>对类的静态变量，静态代码块执行初始化操作</p> <h4 id="双亲委派机制（装载原则）"><a href="#双亲委派机制（装载原则）" class="header-anchor">#</a> 双亲委派机制（装载原则）</h4> <h5 id="类加载源代码解读："><a href="#类加载源代码解读：" class="header-anchor">#</a> 类加载源代码解读：</h5> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">protected</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">&gt;</span></span> <span class="token function">loadClass</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">boolean</span> resolve<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ClassNotFoundException</span>
<span class="token punctuation">{</span>
    <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token function">getClassLoadingLock</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 保证该类只加载一次</span>
        <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">&gt;</span></span> c <span class="token operator">=</span> <span class="token function">findLoadedClass</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>c <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">long</span> t0 <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>parent <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token comment">// 存在父级类加载器则使用父级的父级类加载器进行加载</span>
                    c <span class="token operator">=</span> parent<span class="token punctuation">.</span><span class="token function">loadClass</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                    <span class="token comment">// 不存在父级类加载器则尝试使用BootstrapClassLoader进行加载</span>
                    c <span class="token operator">=</span> <span class="token function">findBootstrapClassOrNull</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token comment">// 抛出类未找到异常说明父类加载器加载失败</span>
            <span class="token punctuation">}</span>


            <span class="token keyword">if</span> <span class="token punctuation">(</span>c <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token comment">// 父级类加载器未完成加载且BootstrapClassLoader也未完成加载则使用自己的加载方式</span>
                <span class="token keyword">long</span> t1 <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                c <span class="token operator">=</span> <span class="token function">findClass</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>


                <span class="token comment">// this is the defining class loader; record the stats</span>
                sun<span class="token punctuation">.</span>misc<span class="token punctuation">.</span><span class="token class-name">PerfCounter</span><span class="token punctuation">.</span><span class="token function">getParentDelegationTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addTime</span><span class="token punctuation">(</span>t1 <span class="token operator">-</span> t0<span class="token punctuation">)</span><span class="token punctuation">;</span>
                sun<span class="token punctuation">.</span>misc<span class="token punctuation">.</span><span class="token class-name">PerfCounter</span><span class="token punctuation">.</span><span class="token function">getFindClassTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addElapsedTimeFrom</span><span class="token punctuation">(</span>t1<span class="token punctuation">)</span><span class="token punctuation">;</span>
                sun<span class="token punctuation">.</span>misc<span class="token punctuation">.</span><span class="token class-name">PerfCounter</span><span class="token punctuation">.</span><span class="token function">getFindClasses</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>resolve<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">resolveClass</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> c<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br></div></div><p>流程解读：</p> <ol><li>类加载器收到类加载的请求</li> <li>将这个请求向上委托给父类加载器去完成，一 直向上委托，知道启动类加载器</li> <li>启动加载器检查是否能够加载当前这个类，能加载就结束， 使用当前的加载器，否则， 抛出异常，通知子加载器进行加载</li> <li>重复步骤3 Class Not Found异常就是这么来的</li></ol> <p>JVM在加载类时默认采用的是<strong>双亲委派</strong>机制。通俗的讲，就是某个特定的类加载器在接到加载类的请求时，首先将加载任务委托给父类加载器，依次递归，如果父类加载器可以完成类加载任务，就成功返回；只有父类加载器无法完成此加载任务时，才自己去加载。</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/image-20210210104716029.png" alt="image-20210210104716029"></p> <p>例如：当jvm要加载Test.class的时候，</p> <blockquote><p>（1）首先会到自定义加载器中查找（其实是看运行时数据区的方法区有没有加载），看是否已经加载过，如果已经加载过，则返回字节码。</p> <p>（2）如果自定义加载器没有加载过，则询问上一层加载器(即AppClassLoader)是否已经加载过Test.class。</p> <p>（3）如果没有加载过，则询问上一层加载器（ExtClassLoader）是否已经加载过。</p> <p>（4）如果没有加载过，则继续询问上一层加载（BoopStrap ClassLoader）是否已经加载过。</p> <p>（5）如果BoopStrap ClassLoader依然没有加载过，则到自己指定类加载路径下（&quot;sun.boot.class.path&quot;）查看是否有Test.class字节码，有则返回，没有则通知下一层加载器ExtClassLoader到自己指定的类加载路径下（java.ext.dirs）查看。</p> <p>（6）依次类推，最后到自定义类加载器指定的路径还没有找到Test.class字节码，则抛出异ClassNotFoundException。</p></blockquote> <h5 id="为什么要使用这种加载方式呢？"><a href="#为什么要使用这种加载方式呢？" class="header-anchor">#</a> 为什么要使用这种加载方式呢？</h5> <blockquote><p>**双亲委派模型是为了保证Java核心库的类型安全。**所有Java应用都至少需要引用java.lang.Object类，在运行时这个类需要被加载到Java虚拟机中。如果该加载过程由自定义类加载器来完成，可能就会存在多个版本的java.lang.Object类，而且这些类之间是不兼容的。</p> <p>通过双亲委派模型，对于Java核心库的类的加载工作由启动类加载器来统一完成，保证了Java应用所使用的都是同一个版本的Java核心库的类，是互相兼容的</p> <p>1，类加载器代码本身也是java类，因此类加载器本身也是要被加载的，因此显然必须有第一个类加载器不是Java类，这就是bootStrap，是使用c++写的其他这是java了。</p> <p>2，虽说bootStrap、extclassLoader、appclassloader三个是父子类加载器关系，但是并没有使用继承，而是使用了组合关系。</p> <p>3，优点，具备了一种带优先级的层次关系，越是基础的类，越是被上层的类加载器进行加载，可以比较笼统的说像jdk自带的几个jar包肯定是位于最顶级的，再就是我们引用的包，最后是我们自己写的，保证了java程序的稳定性。</p></blockquote> <h4 id="自定义类装载器"><a href="#自定义类装载器" class="header-anchor">#</a> 自定义类装载器</h4> <p>​	可以继承ClassLoader类，然后重写其中的loadClass方法（tomcat的WebAppClassLoader就是自定义的装载器，）</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/1584675758175-03e96f1a-8c21-4f5b-b113-bb70a990bb19.jpeg" alt="image"></p> <h3 id="执行引擎（executionengine子系统）："><a href="#执行引擎（executionengine子系统）：" class="header-anchor">#</a> 执行引擎（Executionengine子系统）：</h3> <p>(1)负责执行来自<strong>类加载器子系统（class loader subsystem）中</strong>被加载类中在方法区包含的指令集，通俗讲就是类加载器子系统把代码逻辑（什么时候该if，什么时候该相加，相减）都以指令的形式加载到了方法区，执行引擎就负责执行这些指令就行了。</p> <p>用网上最流行的一张图表示就是：</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/1584675758204-bf8b6180-3c9b-4af8-8877-bed4dcc3b90f.jpeg" alt="img"></p> <p>(1)程序在JVM主要执行的过程是执行引擎与运行时数据区不断交互的过程，可理解为上面“方法区中的动图” （2）但是执行引擎拿到的方法区中的指令还是人能够看懂的，这里执行引擎的工作就是要把指令转成JVM执行的语言（也可以理解成操作系统的语言），最后操作系统语言再转成计算机机器码。</p> <p>（3）</p> <ul><li><strong>解释器</strong>：一条一条地读取，解释并且执行字节码指令。因为它一条一条地解释和执行指令，所以它可以很快地解释字节码，但是执行起来会比较慢。这是解释执行的语言的一个缺点。字节码这种“语言”基本来说是解释执行的。</li> <li><strong>即时(Just-In-Time)编译器</strong>：即时编译器被引入用来弥补解释器的缺点。执行引擎首先按照解释执行的方式来执行，然后在合适的时候，即时编译器把整段字节码编译成本地代码。然后，执行引擎就没有必要再去解释执行方法了，它可以直接通过本地代码去执行它。执行本地代码比一条一条进行解释执行的速度快很多。编译后的代码可以执行的很快，因为本地代码是保存在缓存里的。</li></ul> <p>简单理解jit就是当代码中某些方法复用次数比较高的，并超过一个特定的值就成为了“热点代码”。那么这个这些热点代码就会被编译成本地代码（其实可以理解成缓存）加快访问速度。</p> <h2 id="运行时数据区域-runtimedataarea-组件"><a href="#运行时数据区域-runtimedataarea-组件" class="header-anchor">#</a> 运行时数据区域(Runtimedataarea)组件</h2> <p>官网：https://docs.oracle.com/javase/specs/jvms/se8/html/index.html</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/image-20210210154308735.png" alt="image-20210210154308735"></p> <h3 id="程序计数器（program-counter-register）"><a href="#程序计数器（program-counter-register）" class="header-anchor">#</a> 程序计数器（Program Counter Register）:</h3> <p>也叫PC寄存器，是一块较小的内存空间，它可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。</p> <p>（1）区别于计算机硬件的pc寄存器，两者不略有不同。计算机用pc寄存器来存放“伪指令”或地址，而相对于虚拟机，pc寄存器它表现为一块内存(一个字长，虚拟机要求字长最小为32位)，虚拟机的pc寄存器的功能也是存放伪指令，更确切的说存放的是将要执行指令的地址。</p> <p>（2）当虚拟机正在执行的方法是一个本地（native）方法的时候，jvm的pc寄存器存储的值是undefined。</p> <p>（3）程序计数器是线程私有的，它的生命周期与线程相同，每个线程都有一个。</p> <p>（4）此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。</p> <h3 id="java虚拟机栈（java-virtual-machine-stack）："><a href="#java虚拟机栈（java-virtual-machine-stack）：" class="header-anchor">#</a> Java虚拟机栈（Java Virtual Machine Stack）：</h3> <p>（1）线程私有的，它的生命周期与线程相同，每个线程都有一个。</p> <p>（2）每个线程创建的同时会创建一个JVM栈，JVM栈中每个栈帧存放的为当前线程中局部基本类型的变量（java中定义的八种基本类型：boolean、char、byte、short、int、long、float、double；和reference （32 位以内的数据类型，具体根据JVM位数（64为还是32位）有关，因为一个solt(槽）占用32位的内存空间 ）、部分的返回结果，非基本类型的对象在JVM栈上仅存放一个指向堆上的地址；</p> <p>（3）每一个方法从被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。</p> <p>（5）栈运行原理：栈中的数据都是以栈帧（Stack Frame）的格式存在，栈帧是一个内存区块，是一个数据集，是一个有关方法和运行期数据的数据集，当一个方法A被调用时就产生了一个栈帧F1，并被压入到栈中，A方法又调用了B方法，于是产生栈帧F2也被压入栈，B方法又调用了C方法，于是产生栈帧F3也被压入栈…… 依次执行完毕后，先弹出后进......F3栈帧，再弹出F2栈帧，再弹出F1栈帧。</p> <p>（6）JAVA虚拟机栈的最小单位可以理解为一个个栈帧，一个方法对应一个栈帧，一个栈帧可以执行很多指令，如下图：</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/1584675758216-90994fea-ad30-4957-836f-9238f6490f73.jpeg" alt="image"></p> <p>（7）动态链接 ：</p> <p>当出现main方法需要调用method1()方法的时候，操作指令就会触动这个动态链接就会找到方法区中对应的method1(),然后把method1()方法压入虚拟机栈中，执行method1栈帧的指令；</p> <p>此外如果指令表示的代码是个常量，这也是个动态链接，也会到方法区中的运行时常量池找到类加载时就专门存放变量的运行时常量池的数据。</p> <h3 id="本地方法栈（native-method-stack）："><a href="#本地方法栈（native-method-stack）：" class="header-anchor">#</a> 本地方法栈（Native Method Stack）：</h3> <p>（1）先解释什么是本地方法：jvm中的本地方法是指方法的修饰符是带有native的但是方法体不是用java代码写的一类方法，这类方法存在的意义当然是填补java代码不方便实现的缺陷而提出的。案例介绍将在 下面22知识点仔细介绍。</p> <p>（2）作用同java虚拟机栈类似，区别是：虚拟机栈为虚拟机执行Java方法服务，而本地方法栈则是为虚拟机使用到的Native方法服务。</p> <p>（3）是线程私有的，它的生命周期与线程相同，每个线程都有一个。</p> <h4 id="本地（native）方法讲解："><a href="#本地（native）方法讲解：" class="header-anchor">#</a> 本地（native）方法讲解：</h4> <p>（1）本地方法就是带有native标识符修饰的方法；</p> <p>（2）native修饰符修饰的方法并不提供方法体，但因为其实现体是由非java代码在在外部实现的，因此不能与abstract连用；</p> <p>（3）存在的意义：不方便用java语言写的代码，使用更为专业的语言写更合适；甚至有些JVM的实现就是用c编写的，所以只能使用c来写，</p> <p>（4）更多的本地方法最好是与jdk的执行引擎的解释器语言一致（执行引擎、解释器：参考21的执行引擎）；</p> <p>（5）Windows、Linux、UNIX、<a href="https://www.baidu.com/s?wd=Dos%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F&amp;tn=SE_PcZhidaonwhc_ngpagmjz&amp;rsv_dl=gh_pc_zhidao" target="_blank" rel="noopener noreferrer">Dos操作系统<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>的核心代码大部分是使用C和C＋＋编写，底层接口用汇编编写．</p> <p>（6）为什么native方法修饰的修饰的方法PC程序计数器为undefined。读懂上面的所有知识点可以就很容易自己理解了。在一开始类加载时，native修饰的方法就被保存在了本地方法栈中，当需要调用native方法时，调用的是一个指向本地方法栈中某方法的地址，然后执行方法直接与操作系统交互，返回运行结果。整个过程并没有经过执行引擎的解释器把字节码解释成操作系统语言，PC计数器也就没有起作用。</p> <h3 id="java-堆（java-heap）："><a href="#java-堆（java-heap）：" class="header-anchor">#</a> Java 堆（Java Heap）：</h3> <p>（1）是Java虚拟机所管理的内存中最大的一块。</p> <p>（2）不同于上面3个，堆是jvm所有线程共享的。</p> <p>（3）在虚拟机启动的时候创建。</p> <p>（4）唯一目的就是存放对象实例，几乎所有的对象实例以及数组都要在这里分配内存。</p> <p>（5）Java堆是垃圾收集器管理的主要区域。</p> <p>（6）因此很多时候java堆也被称为“GC堆”（Garbage Collected Heap）。从内存回收的角度来看，由于现在收集器基本都采用分代收集算法，所以Java堆还可以细分为：新生代和老年代；新生代又可以分为：Eden 空间、From Survivor空间、To Survivor空间。</p> <p>（7）java堆是计算机物理存储上不连续的、逻辑上是连续的，也是大小可调节的（通过-Xms和-Xmx控制）。（8）如果在堆中没有内存完成实例的分配，并且堆也无法再扩展时，将会抛出OutOfMemoryError异常。</p> <h4 id="常量池"><a href="#常量池" class="header-anchor">#</a> 常量池</h4> <p><strong>例题分析：</strong></p> <p>例1：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>1. class Test{
2.  public static String s1 = &quot;static&quot;;
3.  public static void main(String[] args) {
4.      String s2 = new String(&quot;he&quot;)+new String(&quot;llo&quot;);
5.      s2.intern();
6.      String s3 = &quot;hello&quot;;
7.      System.out.println(s2==s3);  //true
8.  }
9. }
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>&quot;static&quot; &quot;he&quot; &quot;llo&quot; &quot;hello&quot;都会进入Class常量池，类加载阶段由于解析阶段时lazy的，所以不会创建实例，更不会驻留字符串常量池。但要注意这个“static&quot;和其他三个不一样，它是静态的，在加载阶段的初始化阶段，会为静态遍历执行初始值，也就是将&quot;static&quot;赋值给s1，所以会创建&quot;static&quot;字符串对象， 并且会保存一个指向它的引用到字符串常量池。</p> <p>运行main方法后，执行String s2 = new String(&quot;he&quot;)+new String(&quot;llo&quot;)语句，创建&quot;he&quot;和&quot;llo&quot;的对象，并会保存引用到字符串常量池中，然后内部创建一个StringBuilder对象，一路append,最后调用toString()方法得到一个String对象（内容是hello,注意这个toString方法会new一个String对象），并把它赋值给s2（注意这里没有把hello的引用放入字符串常量池）。</p> <p>然后执行语句:s2.intern()，此时字符串常量池中没有，它会将上面的这个hello对象的引用保存到字符串常量池，然后返回这个引用，但是这个返回的引用没有变量区接收，所以没用。</p> <p>然后执行：String s3 = &quot;hello&quot;因为字符串常量池中已经有了，所以直接指向堆中&quot;hello&quot;对象</p> <p>然后执行：System.out.println(s2==s3)，此时返回true。</p> <p>示意图如下：</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/1584675758216-90994fea-ad30-4957-836f-9238f6490f73.jpeg" alt="image"></p> <p>例题2：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>1. class JianZhiOffer{
2.  public static void main(String[] args) {
3.      String s1 = new String(&quot;he&quot;)+new String(&quot;llo&quot;);    //第一句
4.      String s2 = new String(&quot;h&quot;)+new String(&quot;ello&quot;);    //第二句
5.      String s3 = s1.intern();                           //第三句
6.      String s4 = s2.intern();                           //第四句
7.      System.out.println(s1==s3);                        //第五句
8.      System.out.println(s1==s4);                        //第六句
9.  }
10. }
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>类加载阶段，什么都没干。</p> <p>第一句:创建&quot;he&quot;和&quot;llo&quot;对象，并放入字符串常量池，然后创建&quot;hello&quot;对象，没有放入字符串常量池，s1指向这个&quot;hello&quot; String对象。</p> <p>第二句:创建了&quot;h&quot;和&quot;ello&quot;对象，并放入字符串常量池，然后创建&quot;hello&quot;对象，没有放入字符串常量池，s2指向这个&quot;hello&quot; String对象。</p> <p>第三句:字符串常量池中没有&quot;hello&quot;，所以会把s1指向String对象的引用放入字符串常量池，然后将这个引用返回给了s3，所以s1==s3是true</p> <p>第四句：字符串常量池中有了&quot;hello&quot;，所以将s4指向的s1指向的对象&quot;hello&quot;，所以第六句s4==s1是true。</p> <p>参考：https://blog.csdn.net/luzhensmart/article/details/86565496</p> <h3 id="方法区（method-area）："><a href="#方法区（method-area）：" class="header-anchor">#</a> 方法区（Method Area）：</h3> <div class="language- line-numbers-mode"><pre class="language-text"><code>Student s = new Student(&quot;小明&quot;，18);
s 是指针，存放在栈中。
new Student(&quot;小明&quot;，18) 是对象 ，存放在堆中。
Student 类的信息存放在方法区。
总结 ：
对象的实例保存在堆上，对象的元数据（instantKlass）保存在方法区，对象的引用保存在栈上。
Class对象 和 类加载器 也保存在堆上
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>（1）在虚拟机启动的时候创建。</p> <p>（2）所有jvm线程共享。</p> <p>（3）除了和堆一样不需要不连续的内存空间和可以固定大小或者可扩展外，还可以选择不实现垃圾收集。</p> <p>（5）用于存放已被虚拟机加载的类信息、常量、静态变量、以及编译后的方法实现的二进制形式的机器指令集等数据。</p> <p>（4）被装载的class的信息存储在Methodarea的内存中。当虚拟机装载某个类型时，它使用类装载器定位相应的class文件，然后读入这个class文件内容并把它传输到虚拟机中。</p> <p>（6）运行时常量池（Runtime Constant Pool）是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外，还有一项信息是常量池（Constant Pool Table），用于存放编译期生成的各种字面量和符号引用，这部分内容将在类加载后进入方法区的运行时常量池中存放。</p> <p>方法区补充：</p> <p>指令集是个非常重要概念，因为程序员写的代码其实在jvm虚拟机中是被转成了一条条指令集执行的，看下图</p> <p><img src="https://cdn.jsdelivr.net/gh/yanyunliu/static/img/1584675758216-90994fea-ad30-4957-836f-9238f6490f73.jpeg" alt="image"></p> <p>首先看看上面各部位位于13图中的那些位置：左侧的foo代码是指令集，可见就是在方法区，程序计数器就不用说了，局部变量区位于虚拟机栈中，右侧最下方的求值栈（也就是操作数栈）我们从动图中明显可以看出存在栈顶这个关键词因此也是位于java虚拟机栈的。</p> <p>另外，图中，指令是Java代码经过javac编译后得到的JVM指令，PC寄存器指向下一条该执行的指令地址，局部变量区存储函数运行中产生的局部变量，栈存储计算的中间结果和最后结果。</p> <p>上图的执行的源代码是：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>public class Demo {
    public static void foo() {
       int a = 1;
       int b = 2;
       int c = (a + b) * 5;
    }
}
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>下面简单解释下执行过程，注意：偏移量的数字只是简单代表第几个指令，首先常数1入栈，栈顶元素就是1，然后栈顶元素移入局部变量区存储，常数2入栈，栈顶元素变为2，然后栈顶元素移入局部变量区存储；接着1，2依次再次入栈，弹出栈顶两个元素相加后结果入栈，将5入栈，栈顶两个元素弹出并相乘后结果入栈，然后栈顶变为15，最后移入局部变量。执行return命令如果当前线程对应的栈中没有了栈帧，这个Java栈也将会被JVM撤销。</p> <h4 id="直接内存（堆外内存）"><a href="#直接内存（堆外内存）" class="header-anchor">#</a> 直接内存（堆外内存）</h4> <p>Java中的内存分为两个部分，一部分是不需要jvm管理的直接内存，也被称为堆外内存（<strong>直接内存并不是虚拟机运行时数据区的一部分，也不是虚拟机规范中定义的内存区域</strong>）。堆外内存就是把内存对象分配在JVM堆以外的内存区域，这部分内存不是虚拟机管理，而是由操作系统来管理，这样可以减少垃圾回收对应用程序的影响。**
**</p> <p><strong>使用</strong></p> <blockquote><p>JDK1.4 中新加入的 <strong>NIO(New Input/Output) 类</strong>，引入了一种基于<strong>通道（Channel）</strong> 与<strong>缓存区（Buffer）</strong> 的 I/O 方式，它可以直接使用 Native 函数库直接分配堆外内存，然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能，因为<strong>避免了在 Java 堆和 Native 堆之间来回复制数据</strong>。</p></blockquote> <p><strong>好处</strong></p> <blockquote><p>使用堆外内存的好处是，可以提升性能。比如常规情况下，把java堆内部的数据进行远程发送，需要先把堆内部的数据拷贝到直接内存里面，也就是拷贝到堆外内存，然后在发送。如果把对象分配到直接内存里面，发送的时候就可以省掉复制的哪一步操作。</p> <p>本机直接内存的分配不会收到 Java 堆的限制，但是，既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。</p></blockquote> <p><strong>缺点</strong></p> <blockquote><p>缺点就是没有jvm帮助管理内存，需要我们自己来管理堆外内存，防止内存溢出。为了避免一直没有FULL GC，最终导致物理内存被耗完。我们会指定直接内存的最大值，通过-XX：MaxDirectMemerySize来指定，当达到阈值的时候，调用system.gc来进行一次full gc，把那些没有被使用的直接内存回收掉。</p></blockquote> <p><strong>注意：</strong></p> <blockquote><ul><li>直接内存和堆外内存不一样</li> <li>直接内存本身不受gc的影响，但是由于有对象在堆引用这这块内存，那么受到gc的间接影响，典型的是java的代码里有system.gc去回收。</li> <li>直接内存也和0拷贝不是一回事。</li> <li>直接内存其实是jvm定义的空间，可以说堆，元数据区算在同一层次。</li> <li>参考：https://www.jianshu.com/p/9d41989afea5</li></ul></blockquote> <h2 id="oom（内存溢出）"><a href="#oom（内存溢出）" class="header-anchor">#</a> OOM（内存溢出）</h2> <h3 id="outofmemoryerror：-permgen-space"><a href="#outofmemoryerror：-permgen-space" class="header-anchor">#</a> OutOfMemoryError： PermGen space</h3> <p>发生这种问题的原意是程序中使用了大量的jar或class，使java虚拟机装载类的空间不够，与Permanent Generation space有关。</p> <p>解决办法：</p> <p>增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小。</p> <blockquote><ul><li>XX:PermSize是初始永久保存区域大小，</li> <li>XX:MaxPermSize是最大永久保存区域大小</li> <li>如针对tomcat6.0，在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处（大约在70行左右） 增加一行：JAVA_OPTS=&quot; -XX:PermSize=64M -XX:MaxPermSize=128m&quot;</li> <li>如果是windows服务器还可以在系统环境变量中设置。</li></ul></blockquote> <h3 id="outofmemoryerror：-java-heap-space"><a href="#outofmemoryerror：-java-heap-space" class="header-anchor">#</a> OutOfMemoryError：  Java heap space</h3> <blockquote><p>发生这种问题的原因是java虚拟机创建的对象太多，在进行垃圾回收之间，虚拟机分配的到堆内存空间已经用满了，与Heap space有关。</p></blockquote> <p>解决这类问题有两种思路：</p> <p>\1. 检查程序，看是否有死循环或不必要地重复创建大量对象。找到原因后，修改程序和算法。</p> <p>\2. 增加Java虚拟机中Xms（初始堆大小）和Xmx（最大堆大小）参数的大小。如：set JAVA_OPTS= -Xms256m -Xmx1024m</p> <h3 id="outofmemoryerror：unable-to-create-new-native-thread"><a href="#outofmemoryerror：unable-to-create-new-native-thread" class="header-anchor">#</a> OutOfMemoryError：unable to create new native thread</h3> <h3 id="内存泄露"><a href="#内存泄露" class="header-anchor">#</a> 内存泄露</h3> <h4 id="一：什么是内存泄露"><a href="#一：什么是内存泄露" class="header-anchor">#</a> <strong>一：什么是内存泄露</strong></h4> <p>​	由于java的JVM引入了垃圾回收机制，垃圾回收器会自动回收不再使用的对象，了解JVM回收机制的都知道JVM是使用引用计数法和可达性分析算法来判断对象是否是不再使用的对象，<strong>本质都是判断一个对象是否还被引用</strong>。那么对于这种情况下，由于代码的实现不同就会出现很多种内存泄漏问题（让JVM误以为此对象还在引用中，无法回收，造成内存泄漏）。</p> <h4 id="二：常见的内存泄露造成的原因"><a href="#二：常见的内存泄露造成的原因" class="header-anchor">#</a> <strong>二：常见的内存泄露造成的原因</strong></h4> <h5 id="_1、单例造成的内存泄漏"><a href="#_1、单例造成的内存泄漏" class="header-anchor">#</a> 1、单例造成的内存泄漏</h5> <p>由于单例的静态特性使得其生命周期和应用的生命周期一样长，如果一个对象已经不再需要使用了，而单例对象还持有该对象的引用，就会使得该对象不能被正常回收，从而导致了内存泄漏。
示例：防止单例导致内存泄漏的实例</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>// 使用了单例模式
public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}
1234567891011121314
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><h5 id="_2、非静态内部类创建静态实例造成的内存泄漏"><a href="#_2、非静态内部类创建静态实例造成的内存泄漏" class="header-anchor">#</a> 2、非静态内部类创建静态实例造成的内存泄漏</h5> <p>例如，有时候我们可能会在启动频繁的Activity中，为了避免重复创建相同的数据资源，可能会出现如下写法：</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>  public class MainActivity extends AppCompatActivity {

    private static TestResource mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mResource == null){
            mResource = new TestResource();
        }
        //...
    }
    
    class TestResource {
    //...
    }
}   
123456789101112131415161718
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><h5 id="_3、handler造成的内存泄漏"><a href="#_3、handler造成的内存泄漏" class="header-anchor">#</a> 3、Handler造成的内存泄漏</h5> <p>示例：创建匿名内部类的静态对象</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>public class MainActivity extends AppCompatActivity {

    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ...
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                // ...
                handler.sendEmptyMessage(0x123);
            }
        });
    }
}
1234567891011121314151617181920212223
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br></div></div><p>1、从Android的角度
当Android应用程序启动时，该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。当主线程中实例化一个Handler对象后，它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用，所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message，Looper就会不断的从中取出并交给Handler处理。另外，主线程的Looper对象会伴随该应用程序的整个生命周期。
2、 Java角度
在Java中，非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用，但是静态内部类却不会。
对上述的示例进行分析，当MainActivity结束时，未处理的消息持有handler的引用，而handler又持有它所属的外部类也就是MainActivity的引用。这条引用关系会一直保持直到消息得到处理，这样阻止了MainActivity被垃圾回收器回收，从而造成了内存泄漏。
解决方法：将Handler类独立出来或者使用静态内部类，这样便可以避免内存泄漏。</p> <h5 id="_4、线程造成的内存泄漏"><a href="#_4、线程造成的内存泄漏" class="header-anchor">#</a> 4、线程造成的内存泄漏</h5> <p>示例：AsyncTask和Runnable</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new MyRunnable()).start();
        new MyAsyncTask(this).execute();
    }

    class MyAsyncTask extends AsyncTask&lt;Void, Void, Void&gt; {

        // ...

        public MyAsyncTask(Context context) {
            // ...
        }

        @Override
        protected Void doInBackground(Void... params) {
            // ...
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            // ...
        }
    }

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // ...
        }
    }
}
1234567891011121314151617181920212223242526272829303132333435363738
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br></div></div><p>AsyncTask和Runnable都使用了匿名内部类，那么它们将持有其所在Activity的隐式引用。如果任务在Activity销毁之前还未完成，那么将导致Activity的内存资源无法被回收，从而造成内存泄漏。
解决方法：将AsyncTask和Runnable类独立出来或者使用静态内部类，这样便可以避免内存泄漏。</p> <h5 id="_5、资源未关闭造成的内存泄漏"><a href="#_5、资源未关闭造成的内存泄漏" class="header-anchor">#</a> 5、资源未关闭造成的内存泄漏</h5> <p>对于使用了BraodcastReceiver，ContentObserver，File，Cursor，Stream，Bitmap等资源，应该在Activity销毁时及时关闭或者注销，否则这些资源将不会被回收，从而造成内存泄漏。
1）比如在Activity中register了一个BraodcastReceiver，但在Activity结束后没有unregister该BraodcastReceiver。
2）资源性对象比如Cursor，Stream、File文件等往往都用了一些缓冲，我们在不使用的时候，应该及时关闭它们，以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内，还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null，而不关闭它们，往往会造成内存泄漏。
3）对于资源性对象在不使用的时候，应该调用它的close()函数将其关闭掉，然后再设置为null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。
4）Bitmap对象不在使用时调用recycle()释放内存。2.3以后的bitmap应该是不需要手动recycle了，内存已经在java层了。</p> <h5 id="_6、集合容器中的内存泄露"><a href="#_6、集合容器中的内存泄露" class="header-anchor">#</a> 6、集合容器中的内存泄露</h5> <p>我们通常把一些对象的引用加入到了集合容器（比如ArrayList）中，当我们不需要该对象时，并没有把它的引用从集合中清理掉，这样这个集合就会越来越大。如果这个集合是static的话，那情况就更严重了。
解决方法：在退出程序之前，将集合里的东西clear，然后置为null，再退出程序。</p> <h4 id="三：如何避免内存泄漏？"><a href="#三：如何避免内存泄漏？" class="header-anchor">#</a> <strong>三：如何避免内存泄漏？</strong></h4> <p>1、平常养成良好的代码书写习惯，该销毁的对象要销毁比如destory啊 广播啊 ，涉及到要用到content上下文的优先考虑全局上线文对象。</p></div></div> <div class="page-edit"><!----> <!----> <!----></div> <div class="page-nav-wapper"><!----> <!----></div></div> <div class="article-list"><div class="article-title"><a href="/archives/" class="iconfont icon-bi">最近更新</a></div> <div class="article-wrapper"><dl><dd>01</dd> <dt><a href="/pages/b06af3/"><div>碎片笔记</div></a> <span>12-12</span></dt></dl><dl><dd>02</dd> <dt><a href="/pages/f176f8/"><div>soft技巧</div></a> <span>12-12</span></dt></dl><dl><dd>03</dd> <dt><a href="/pages/5390d7/"><div>HTTP连接方式</div></a> <span>12-12</span></dt></dl> <dl><dd></dd> <dt><a href="/archives/" class="more">更多文章&gt;</a></dt></dl></div></div> </main></div> <div class="footer"><div class="icons"><a href="mailto:17519412931@163.com" title="发邮件" target="_blank" class="iconfont icon-youjian"></a><a href="https://gitee.com/yanyunliu" title="Gitee" target="_blank" class="iconfont icon-github"></a><a href="https://y.qq.com/portal/player.html" title="听音乐" target="_blank" class="iconfont icon-erji"></a></div> 
  Theme by
  <a href="https://github.com/xugaoyi/vuepress-theme-vdoing" target="_blank" title="本站主题">Vdoing</a> <!----></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div> <div title="主题模式" class="button blur theme-mode-but iconfont icon-zhuti"><ul class="select-box" style="display:none;"><li class="iconfont icon-zidong">跟随系统</li><li class="iconfont icon-rijianmoshi">浅色模式</li><li class="iconfont icon-yejianmoshi">深色模式</li><li class="iconfont icon-yuedu">阅读模式</li></ul></div></div> <div class="body-bg" style="background:url() center center / cover no-repeat;opacity:0.5;"></div></div><div class="global-ui"><div></div></div></div>
    <script src="/assets/js/app.c45b0dab.js" defer></script><script src="/assets/js/2.921829d9.js" defer></script><script src="/assets/js/92.812c6c6e.js" defer></script>
  </body>
</html>